package logic

import (
	"bluebell/dao/mysql"
	"bluebell/dao/redis"
	"bluebell/models"
	"bluebell/pkg/snowflake"
	"errors"
	"go.uber.org/zap"
	"strconv"
)

// CreatePost 创建帖子
func CreatePost(post *models.Post)(err error){
	// 1、 生成post_id(生成帖子ID)
	postId, err := snowflake.GetID()
	if err != nil {
		zap.L().Error("snowflake.GetID() failed", zap.Error(err))
		return
	}
	post.PostId = postId
	// 2、创建帖子 保存到数据库
	if err := mysql.CreatePost(post); err != nil {
		zap.L().Error("mysql.CreatePost(&post) failed", zap.Error(err))
		return err
	}
	// redis存储帖子信息
	if err := redis.CreatePost(post.PostId,post.AuthorId,post.Title,TruncateByWords(post.Content, 120),post.CommunityId);err !=nil{
		zap.L().Error("redis.CreatePost failed", zap.Error(err))
		return err
	}
	return
}

// GetPostDetailList 分页获取帖子列表
// 数据库错误 ---> tx.Error
// 成功 ---> data,nil
func GetPostDetailList(page,size int64) (data []*models.PostDetail,err error){
	postList,err := mysql.GetPostList(page,size)
	if err != nil{
		err = errors.New(mysql.ErrorQueryFailed)
		zap.L().Error("logic mysql.GetPostList() failed",zap.Error(err))
		return nil,err
	}
	data = make([]*models.PostDetail,0,len(postList))
	for _,post := range postList{
		// 根据 AuthorId 查看作者信息
		user,err := mysql.GetUserById(post.AuthorId)
		if err != nil{
			err = errors.New(mysql.ErrorQueryFailed)
			zap.L().Error("logic mysql.GetUserById() failed",zap.Error(err))
			continue
		}
		// 根据社区id查询社区详细信息
		community, err := mysql.GetCommunityById(post.CommunityId)
		if err != nil {
			err = errors.New(mysql.ErrorQueryFailed)
			zap.L().Error("logic mysql.GetCommunityById() failed",zap.Error(err))
			continue
		}
		// 根据帖子id查询帖子的投票数
		voteNum, _ := redis.GetPostVoteNum(int64(post.PostId))
		// 接口数据拼接
		postDetail := &models.PostDetail{
			Post:               post,
			CommunityDetailRes: community,
			AuthorName:         user.UserName,
			VoteNum: 			voteNum,
		}
		data = append(data, postDetail)
	}
	return data, nil
}

// GetSortPostListByCommunityId 根据社区id(id == 0则是所有的帖子) ---> 并以创建时间或者分数排序 分页展示帖子列表
func GetSortPostListByCommunityId(p *models.ParamPostList) (data *models.PostDetailRes,err error){
	// 根据请求参数的不同,执行不同的业务逻辑
	if p.CommunityId == 0{
		// 查所有
		data,err = GetAllPostList(p)
	}else{
		// 根据社区id查询
		data,err = GetPostListByCommunityId(p)
	}
	if err != nil {
		zap.L().Error("GetPostListNew failed", zap.Error(err))
		return nil, err
	}
	return data, nil
}
// GetAllPostList  社区id == 0 --->以创建时间或者分数排序 分页获取帖子列表
func GetAllPostList(p *models.ParamPostList)(*models.PostDetailRes,error){
	res := &models.PostDetailRes{}
	// 从mysql获取帖子列表总数
	total,err := mysql.GetPostTotalCount()
	if err != nil{
		err = errors.New(mysql.ErrorQueryFailed)
		zap.L().Error("logic post mysql.GetAllPostList() failed",zap.Error(err))
		return nil,err
	}
	res.Page.Total = total
	// 1、根据参数中的排序规则去redis查询id列表
	postIdS,err := redis.GetPostIdsInOrder(p)
	if err != nil{
		zap.L().Error("logic post redis.GetPostIdsInOrder() failed",zap.Error(err))
		return nil,redis.ErrQueryFailed
	}
	if len(postIdS) == 0{
		zap.L().Warn("logic post redis.GetPostIDsInOrder(p) return 0 data")
		return res,nil
	}
	// 2、提前查询好每篇帖子的投票数
	voteData,err := redis.GetPostVoteData(postIdS)
	if err != nil{
		zap.L().Error("logic post redis.GetPostVoteData() failed",zap.Error(err))
		return nil,redis.ErrQueryFailed
	}
	// 3、根据id去数据库查询帖子详细信息
	// 返回的数据还要按照给定的id的顺序返回
	posts,err := mysql.GetPostListByIds(postIdS)
	if err  != nil{
		err = errors.New(mysql.ErrorQueryFailed)
		zap.L().Error("logic post mysql.GetPostListByIds()",zap.Error(err))
		return nil,redis.ErrQueryFailed
	}
	res.Page.Page = p.Page
	res.Page.Size = p.Size
	res.List = make([]*models.PostDetail, 0, len(posts))
	// 4、组合数据
	// 将帖子的作者及分区信息查询出来填充到帖子中
	for i,post := range posts{
		// 根据作者id查询作者信息
		user, err := mysql.GetUserById(post.AuthorId)
		if err != nil{
			err := errors.New(mysql.ErrorQueryFailed)
			zap.L().Error("mysql.GetUserByID() failed", zap.Uint64("post_id", post.AuthorId), zap.Error(err))
			continue
		}
		// 根据社区id查询社区详细信息
		community, err := mysql.GetCommunityById(post.CommunityId)
		if err != nil {
			err := errors.New(mysql.ErrorQueryFailed)
			zap.L().Error("mysql.GetCommunityByID() failed",zap.Uint64("community_id", post.CommunityId),zap.Error(err))
			continue
		}
		// 接口数据拼接
		postDetail := &models.PostDetail{
			VoteNum:            voteData[i],
			Post:               post,
			CommunityDetailRes: community,
			AuthorName:         user.UserName,
		}
		res.List = append(res.List, postDetail)
	}
	return res, nil
}
// GetPostListByCommunityId  根据社区id查询 ---> 并以创建时间或者分数排序 分页展示帖子列表
func GetPostListByCommunityId(p *models.ParamPostList)(*models.PostDetailRes,error){
	var res models.PostDetailRes
	// 从mysql获取该社区下帖子列表总数
	total, err := mysql.GetCommunityPostTotalCount(p.CommunityId)
	if err != nil {
		err = errors.New(mysql.ErrorQueryFailed)
		zap.L().Error("logic post mysql.GetCommunityPostTotalCount() failed",zap.Error(err))
		return nil, err
	}
	res.Page.Total = total
	// 1、根据参数中的排序规则去redis查询id列表
	ids, err := redis.GetCommunityPostIdsInOrder(p)
	if err != nil {
		zap.L().Error("logic post redis.GetCommunityPostList() failed",zap.Error(redis.ErrQueryFailed))
		return nil, redis.ErrQueryFailed
	}
	if len(ids) == 0 {
		zap.L().Warn("logic post redis.GetCommunityPostList() return 0 data")
		return &res, nil
	}
	// 2、提前查询好每篇帖子的投票数
	voteData, err := redis.GetPostVoteData(ids)
	if err != nil {
		return nil, err
	}
	// 3、根据id去数据库查询帖子详细信息
	// 返回的数据还要按照我给定的id的顺序返回  order by FIND_IN_SET(post_id, ?)
	posts, err := mysql.GetPostListByIds(ids)
	if err != nil {
		return nil, err
	}
	res.Page.Page = p.Page
	res.Page.Size = p.Size
	res.List = make([]*models.PostDetail, 0, len(posts))
	// 4、根据社区id查询社区详细信息
	// 为了减少数据库的查询次数，这里将社区信息提前查询出来
	community, err := mysql.GetCommunityById(p.CommunityId)
	if err != nil {
		zap.L().Error("mysql.GetCommunityByID() failed",zap.Uint64("community_id", p.CommunityId), zap.Error(err))
		community = nil
	}
	for idx, post := range posts {
		// 根据作者id查询作者信息
		user, err := mysql.GetUserById(post.AuthorId)
		if err != nil {
			zap.L().Error("mysql.GetUserByID() failed",
				zap.Uint64("postID", post.AuthorId),
				zap.Error(err))
			user = nil
		}
		// 接口数据拼接
		postDetail := &models.PostDetail{
			VoteNum:            voteData[idx],
			Post:               post,
			CommunityDetailRes: community,
			AuthorName:         user.UserName,
		}
		res.List = append(res.List, postDetail)
	}
	return &res, nil
}

// GetPostById 根据post_id查询帖子详情
func GetPostById(postId int64)(data *models.PostDetail,err error){
	// 查询帖子信息
	post,err := mysql.GetPostById(postId)
	if err != nil{
		err = errors.New(mysql.ErrorQueryFailed)
		zap.L().Error("logic mysql.GetPostById() failed",zap.Int64("post_id", postId),zap.Error(err))
		return nil,err
	}
	// 根据作者id查询作者信息
	user,err := mysql.GetUserById(post.AuthorId)
	if err != nil{
		err = errors.New(mysql.ErrorQueryFailed)
		zap.L().Error("logic post mysql.GetUserById() failed",zap.Uint64("user_id",post.AuthorId),zap.Error(err))
		return nil,err
	}
	// 根据社区id查询社区详细信息
	community, err := mysql.GetCommunityById(post.CommunityId)
	if err != nil {
		err = errors.New(mysql.ErrorQueryFailed)
		zap.L().Error("logic post mysql.GetCommunityByID() failed", zap.Uint64("community_id", post.CommunityId), zap.Error(err))
		return
	}
	// 根据帖子id查询帖子的投票数
	voteNum, _ := redis.GetPostVoteNum(postId)

	// 接口数据拼接
	data = &models.PostDetail{
		Post:               post,
		CommunityDetailRes: community,
		AuthorName:         user.UserName,
		VoteNum:            voteNum,
	}
	return data, nil
}

// PostSearch 搜索业务-搜索帖子
func PostSearch(p *models.ParamPostList)(*models.PostDetailRes, error) {
	var res models.PostDetailRes
	// 根据搜索条件去mysql查询符合条件的帖子列表总数
	total, err := mysql.GetPostListTotalCount(p)
	if err != nil {
		return nil, err
	}
	res.Page.Total = total
	res.Page.Size = p.Size
	res.Page.Page = p.Page
	// 1、根据搜索条件去mysql分页查询符合条件的帖子列表
	posts, err := mysql.GetPostListByKeywords(p)
	if err != nil {
		return nil, err
	}
	// 查询出来的帖子总数可能为0
	if len(posts) == 0 {
		return &models.PostDetailRes{}, nil
	}
	// 2、查询出来的帖子id列表传入到redis接口获取帖子的投票数
	ids := make([]string, 0, len(posts))
	for _, post := range posts {
		ids = append(ids, strconv.Itoa(int(post.PostId)))
	}
	voteData, err := redis.GetPostVoteData(ids)
	if err != nil {
		return nil, err
	}
	// 3、拼接数据
	res.List = make([]*models.PostDetail, 0, len(posts))
	for idx, post := range posts {
		// 根据作者id查询作者信息
		user, err := mysql.GetUserById(post.AuthorId)
		if err != nil {
			zap.L().Error("mysql.GetUserById() failed",zap.Uint64("post_Id", post.AuthorId), zap.Error(err))
			user = nil
		}
		// 根据社区id查询社区详细信息
		community, err := mysql.GetCommunityById(post.CommunityId)
		if err != nil {
			zap.L().Error("mysql.GetCommunityById() failed",zap.Uint64("community_id", post.CommunityId),zap.Error(err))
			community = nil
		}
		// 接口数据拼接
		postDetail := &models.PostDetail{
			VoteNum:            voteData[idx],
			Post:               post,
			CommunityDetailRes: community,
			AuthorName:         user.UserName,
		}
		res.List = append(res.List, postDetail)
	}
	return &res, nil
}